
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include <linux/mod_devicetable.h>
#include <linux/of_device.h>

#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>



static int major;
static struct class *sr04_class;
static struct gpio_desc *sr04_trig;
static struct gpio_desc *sr04_echo;



DECLARE_WAIT_QUEUE_HEAD(sr04_wait_queue);  			 /*创建并初始化等待队列*/

static int sr04_open(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int sr04_read(struct file *file, char __user *buf, size_t size, loff_t *offsize)
{
	int timeout_us = 1000000;   //设置超时间，防止系统卡死（如何未连接这个传感器）
	unsigned long flags;
	int ret;
	u64 time_ns;
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	local_irq_save(flags);  //关中断
	/*1.启动超声波模块*/
	gpiod_set_value(sr04_trig, 1);
	udelay(10);
	gpiod_set_value(sr04_trig, 0);
	
    /*2、等待回应引脚高电平*/
	while(!gpiod_get_value(sr04_echo) && timeout_us)    //超时判断，防止系统卡死（如何未连接这个传感器）
	{
		udelay(1);
		timeout_us--;
	}
	
	if(timeout_us == 0)
	{
		local_irq_restore(flags);  //超时退出时，开中断
		return -EAGAIN;
	}

	time_ns = ktime_get_ns();
	
	 /*3.等待回应引脚低电平*/
	timeout_us = 1000000;
	while(gpiod_get_value(sr04_echo) && timeout_us)
	{
		udelay(1);     
		timeout_us--;
	}
	
	if(timeout_us == 0)
	{
		local_irq_restore(flags);  //超时退出时，开中断
		return -EAGAIN;
	}
	
	time_ns = ktime_get_ns() - time_ns;   //高电平时间计时
	
	local_irq_restore(flags);
	
	ret = copy_to_user(buf, &time_ns, 4);
	
	mdelay(60);   //测量周期间隔60ms

	
	return 4;
}

static int sr04_close(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations sr04_ops = {
	.owner  = THIS_MODULE,
	.open    = sr04_open,
	.release = sr04_close,
	.read    = sr04_read,
};



static struct of_device_id of_sr04_match_table[] = {
	{ .compatible = "zmq,sr04", },
	{ },
};

static int sr04_probe(struct platform_device *pdev)
{

	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	sr04_trig = gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
	sr04_echo = gpiod_get(&pdev->dev, "echo", GPIOD_IN);

	device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "zmq_sr04");
	return 0;
	
}


static int sr04_remove(struct platform_device *dev)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(sr04_class, MKDEV(major, 0));
	gpiod_put(sr04_trig);   //释放gpio
	gpiod_put(sr04_echo);
	
	return 0;
}


static struct platform_driver sr04_platform_drv = {
	.probe = sr04_probe,
	.remove = sr04_remove,
	.driver = 	{
		.name = "sr04",
		.of_match_table = of_sr04_match_table,
	},

};



static  int  sr04_init(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "zmq_sr04_chrdrv", &sr04_ops);
	sr04_class = class_create(THIS_MODULE, "zmq_sr04_class");
	if(IS_ERR(sr04_class))
	{
		unregister_chrdev(major, "zmq_sr04_chrdrv");
		return PTR_ERR(sr04_class);
	}
	platform_driver_register(&sr04_platform_drv);
	
	return 0;
}


static void sr04_exit(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);

	platform_driver_unregister(&sr04_platform_drv);
	class_destroy(sr04_class);
	unregister_chrdev(major, "zmq_sr04_chrdev");
}


module_init(sr04_init);
module_exit(sr04_exit);
MODULE_LICENSE("GPL");






